package com.cyd.spring;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @version 1.0.0
 * Created by cyd on 2024-07-31 10:19:04
 * @ClassName : com.cyd.spring.CydApplicationContext
 * @Description : 类描述
 */
public class CydApplicationContext {

    private final Class<?> configClass;

    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    private ConcurrentHashMap<String, Object> singletonObjMap = new ConcurrentHashMap<>();

    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();


    /**
     * 构造方法
     *
     * @param configClass
     */
    public CydApplicationContext(Class<?> configClass) {
        this.configClass = configClass;
        System.out.println(this.getClass().getSimpleName() + ":容器无参构造：获取配置文件class对象：configClass");

        // 扫描并创建beanDefinition-装入beanDefMap
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan csAnnotation = configClass.getAnnotation(ComponentScan.class);

            // 获得扫描路径
            String path = csAnnotation.value();
            System.out.println(this.getClass().getSimpleName() + ":获取配置的注解扫描路径：" + path);

            path = path.replace(".", "/");

            // 类加载器提供了类加载URL路径
            ClassLoader classLoader = this.getClass().getClassLoader();
            URL resource = classLoader.getResource(path);
            System.out.println(this.getClass().getSimpleName() + ":获取扫描路径下的文件起点 " + resource);

            File file = new File(resource.getFile());
            // 扫描文件，并创建beanDefinition
            if (file.isDirectory()) {
                // 获得所有文件
                File[] files = file.listFiles();
                System.out.println(this.getClass().getSimpleName() + ":获取所有扫描文件路径 " + Arrays.toString(files));

                for (File f : files) {
                    String fileName = f.getAbsolutePath();
                    if (fileName.endsWith(".class")) {
                        // 获得全类名
                        String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));
                        className = className.replace("\\", ".");
                        System.out.println(this.getClass().getSimpleName() + ":获取全类名 " + className);

                        try {
                            // 类加载器 加载类获得class对象
                            Class<?> clazz = classLoader.loadClass(className);
                            System.out.println(this.getClass().getSimpleName() + ":获取类class对象 " + clazz);

                            // 扫描@Component注解
                            if (clazz.isAnnotationPresent(Component.class)) {

                                // 该类是否实现了BeanPostProcessor接口
                                if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                    BeanPostProcessor instance = (BeanPostProcessor) clazz.newInstance();

                                    beanPostProcessorList.add(instance);
                                }

                                Component componentAnno = clazz.getAnnotation(Component.class);
                                String beanName = componentAnno.value();

                                if ("".equals(beanName)) {
                                    beanName = Introspector.decapitalize(clazz.getSimpleName());
                                }
                                System.out.println(this.getClass().getSimpleName() + ":获取beanName " + clazz);


                                // beanDefinition
                                BeanDefinition beanDefinition = new BeanDefinition();
                                beanDefinition.setType(clazz);

                                // 处理Scope注解
                                if (clazz.isAnnotationPresent(Scope.class)) {
                                    Scope scopeAnno = clazz.getAnnotation(Scope.class);
                                    beanDefinition.setScope(scopeAnno.value());
                                } else {
                                    // 没有@Scope，默认是单例
                                    beanDefinition.setScope(BeanScope.SINGLETON);
                                }
                                System.out.println(this.getClass().getSimpleName() + ":获取类beanDefinition对象 " + beanDefinition);

                                beanDefinitionMap.put(beanName, beanDefinition);
                            }
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InstantiationException e) {
                            e.printStackTrace();
                        }


                    }
                }
            }
        }

        // 实例化单例bean
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if (beanDefinition.getScope().equals(BeanScope.SINGLETON)) {

                Object bean = createBean(beanName, beanDefinition);
                System.out.println(this.getClass().getSimpleName() + "：创建bean对象 完成 " + bean);

                singletonObjMap.put(beanName, bean);
            }
        }

    }


    /**
     * @Author: cyd
     * @Description: 创建的时候就依赖注入
     * @DateTime: 2024/7/31
     */
    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getType();
        if (singletonObjMap.containsKey(beanName)) {
            return singletonObjMap.get(beanName);
        }

        try {
            // 通过反射实例化对象
            Object instance = clazz.getConstructor().newInstance();
            System.out.println(this.getClass().getSimpleName() + "：通过反射实例化对象  " + instance);

            // 给对象赋值，依赖注入
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    field.setAccessible(true);
                    field.set(instance, getBean(field.getName()));
                    System.out.println(this.getClass().getSimpleName() + "：注入属性  " + field);
                }
            }
            System.out.println(this.getClass().getSimpleName() + "：完成依赖注入  " + instance);

            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                if (method.isAnnotationPresent(PostConstruct.class)) {
                    method.setAccessible(true);
                    method.invoke(instance, null);
                    System.out.println(this.getClass().getSimpleName() + instance + "：完成PostConstruct  " + Arrays.toString(methods));

                }
            }


            // Aware回调
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware) instance).setBeanName(beanName);
            }
            System.out.println(this.getClass().getSimpleName() + "：完成BeanNameAware回调  " + instance);


            // 初始化前，BeanPostProcessor bean增强器   aop
            for (BeanPostProcessor postProcessor : beanPostProcessorList) {
                instance = postProcessor.postProcessBeforeInitialization(beanName, instance);
            }
            System.out.println(this.getClass().getSimpleName() + "：完成前置增强  " + instance);


            // 初始化InitializeBean
            if (instance instanceof InitializeBean) {
                ((InitializeBean) instance).afterPropertiesSet();
            }
            System.out.println(this.getClass().getSimpleName() + "：完成初始化  " + instance);


            // 初始化后，BeanPostProcessor bean增强器    aop
            for (BeanPostProcessor postProcessor : beanPostProcessorList) {
                instance = postProcessor.postProcessAfterInitialization(beanName, instance);
            }
            System.out.println(this.getClass().getSimpleName() + "：完成后置增强  " + instance);


            return instance;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @Author: cyd
     * @Description: getBean，获取不到bean就创建一个新的
     * beanName：自定义的beanName，没有自定义就用默认的
     * @DateTime: 2024/7/31
     */
    public Object getBean(String beanName) {
        System.out.println(this.getClass().getSimpleName() + "：试图获取bean  " + beanName);

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition == null) {
            throw new NullPointerException("beanDefinition不能为null");
        } else {
            BeanScope scope = beanDefinition.getScope();

            if (scope.equals(BeanScope.SINGLETON)) {
                Object bean = singletonObjMap.get(beanName);
                // 如果没找到，就创建个新的
                if (bean == null) {
                    Object o = createBean(beanName, beanDefinition);
                    singletonObjMap.put(beanName, o);
                }
                return bean;
            } else {
                // 多例，每次创建新的
                return createBean(beanName, beanDefinition);
            }
        }
    }
}
